import numpy as np
import csv
import os

# 读取数据并预处理
def load_data(file_path):
    data = []
    with open(file_path, 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            if len(row) == 14:  # 确保每行包含14个元素（1个标签+13个特征）
                label = int(row[0])
                features = list(map(float, row[1:]))
                data.append((label, features))
    return data

# 筛选类别为 1 和 2 的数据
def filter_data(data):
    filtered_data = [(label, features) for label, features in data if label in [1, 2]]
    return filtered_data

# 提取特征矩阵和标签
def extract_features_and_labels(filtered_data):
    X = np.array([features for label, features in filtered_data])  # 特征矩阵
    y = np.array([label for label, features in filtered_data])     # 标签向量
    return X, y

# PCA 实现（无监督降维）
def pca(X, n_components=2):
    # 中心化数据（减去均值）
    X_centered = X - np.mean(X, axis=0)
    
    # 计算协方差矩阵
    cov_matrix = np.cov(X_centered, rowvar=False)
    
    # 求解特征值和特征向量
    eigen_values, eigen_vectors = np.linalg.eig(cov_matrix)
    
    # 按特征值降序排序特征向量
    sorted_indices = np.argsort(eigen_values)[::-1]
    eigen_vectors_sorted = eigen_vectors[:, sorted_indices]
    
    # 选择前n_components个特征向量作为投影矩阵
    projection_matrix = eigen_vectors_sorted[:, :n_components]
    
    # 投影到低维空间
    X_pca = X_centered @ projection_matrix
    
    return X_pca

# LDA 实现（有监督降维）
def lda(X, y, n_components=2):
    # 确保数据为浮点类型
    X = X.astype(float)
    
    # 获取类别信息
    classes = np.unique(y)
    n_features = X.shape[1]
    
    # 计算总体均值
    overall_mean = np.mean(X, axis=0)
    
    # 计算类内散度矩阵 S_w
    S_w = np.zeros((n_features, n_features))
    for c in classes:
        X_c = X[y == c]
        mean_c = np.mean(X_c, axis=0)
        S_w += (X_c - mean_c).T @ (X_c - mean_c)
    
    # 检查S_w是否正定
    if not np.all(np.linalg.eigvals(S_w) > 0):
        print("S_w 不是正定矩阵，无法进行 LDA。")
        return None
    
    # 计算类间散度矩阵 S_b
    S_b = np.zeros((n_features, n_features))
    for c in classes:
        X_c = X[y == c]
        mean_c = np.mean(X_c, axis=0)
        n_c = X_c.shape[0]
        S_b += n_c * (mean_c - overall_mean).T @ (mean_c - overall_mean)
    
    # 求解广义特征值问题
    try:
        inv_S_w = np.linalg.inv(S_w)
    except np.linalg.LinAlgError:
        print("S_w 不可逆，无法进行 LDA。")
        return None
    
    # 计算特征值和特征向量（确保实数结果）
    eigen_values, eigen_vectors = np.linalg.eigh(inv_S_w @ S_b)
    
    # 按特征值降序排序
    sorted_indices = np.argsort(eigen_values)[::-1]
    eigen_vectors_sorted = eigen_vectors[:, sorted_indices]
    
    # 选择投影矩阵
    projection_matrix = eigen_vectors_sorted[:, :n_components]
    
    # 投影到低维空间
    X_lda = X @ projection_matrix
    
    return X_lda

# 主函数：执行数据加载、降维和结果展示
def main():
    # 数据文件路径（需根据实际情况修改）
    file_path = r'D:\Python学习\python01\pca_lda\wine.data'
    
    # 检查文件是否存在
    if not os.path.exists(file_path):
        print(f"文件 {file_path} 不存在，请确保已下载并保存该文件。")
        return
    
    # 数据处理流程
    data = load_data(file_path)
    filtered_data = filter_data(data)
    X, y = extract_features_and_labels(filtered_data)
    
    # PCA降维及结果展示
    X_pca = pca(X)
    print("PCA 降维后的二维特征：")
    print(X_pca[:5])  # 展示前5条数据
    
    # LDA降维及结果展示
    X_lda = lda(X, y)
    if X_lda is not None:
        print("\nLDA 降维后的二维特征：")
        print(X_lda[:5])  # 展示前5条数据
    else:
        print("LDA 降维失败。")

if __name__ == "__main__":
    main()